package instance

import (
	"context"
	"errors"
	"fmt"
	"redis-push/pkg/config"
	"redis-push/pkg/db"
	"strings"
	"time"

	"github.com/go-redis/redis/v8"
	"gopkg.in/olivere/elastic.v6"
	"gorm.io/gorm"
)

// Instance 实例记录
type Instance struct {
	ID       uint   `gorm:"column:id;not null;autoIncrement;primaryKey;comment:主键"`
	Host     string `gorm:"column:host;type:varchar(255);comment:主机"`
	Port     uint   `gorm:"column:port;comment:连接端口"`
	Password string `gorm:"column:password;comment:密码"`
	Tag      string `gorm:"column:tags;comment:标签"`
}

// TableName 设置表名
func (*Instance) TableName() string {
	return "redis_instances"
}

// Form 表单
type Form struct {
	Host     string
	Port     uint
	Password string
}

func Create(form *Form) (err error) {
	err = db.New().Select("id").Where("host = ? AND port = ?", form.Host, form.Port).
		Take(&Instance{}).Error
	if err == nil {
		return errors.New("实例已经存在")
	}
	if err == gorm.ErrRecordNotFound {
		err = db.New().Create(Instance{
			Host:     form.Host,
			Port:     form.Port,
			Password: form.Password,
		}).Error
	}
	return
}

// GetSlowlogs 获取实例下的慢日志
func (instance *Instance) GetSlowlogs() (slowLogs []SlowLog, err error) {
	client := redis.NewClient(&redis.Options{
		Addr:        fmt.Sprintf("%s:%d", instance.Host, instance.Port),
		Password:    instance.Password,
		DialTimeout: 3 * time.Second,
	})
	defer client.Close()
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	v, err := client.Do(ctx, "SLOWLOG", "GET").Result()
	if err == nil {
		slowLogs = []SlowLog{}
		logs := v.([]interface{})
		for i := range logs {
			items := logs[i].([]interface{})
			slowLog := SlowLog{
				Host: instance.Host,
				Port: instance.Port,
				Tag:  instance.Tag,
			}
			if len(items) > 3 {
				if v, ok := items[0].(int64); ok {
					slowLog.ID = v
				}
				if v, ok := items[1].(int64); ok {
					slowLog.Timestamp = time.Unix(v, 0)
				}
				if v, ok := items[2].(int64); ok {
					slowLog.DurationMicroseconds = v
				}
				if v, ok := items[3].([]interface{}); ok {
					commandArgs := []string{}
					for i := range v {
						commandArgs = append(commandArgs, fmt.Sprintf("%v", v[i]))
					}
					slowLog.Command = strings.Join(commandArgs, " ")
				}
				if len(items) == 6 {
					if v, ok := items[4].(string); ok {
						slowLog.ClientHost = v
					}
					if v, ok := items[5].(string); ok {
						slowLog.ClientName = v
					}
				}
				slowLogs = append(slowLogs, slowLog)
			}
		}
		if len(slowLogs) > 0 {
			_, err = client.Do(context.Background(), "SLOWLOG", "RESET").Result()
		}
	}
	return
}

// PushSlowlogsToES 保存慢日志到ES
func PushSlowlogsToES(logs []SlowLog, config *config.ESConfig) (err error) {
	if len(logs) == 0 {
		return
	}
	options := []elastic.ClientOptionFunc{elastic.SetSniff(config.SetSniff), elastic.SetURL(strings.Split(config.Hosts, ",")...)}
	if config.User != "" {
		options = append(options, elastic.SetBasicAuth(config.User, config.Password))
	}
	client, err := elastic.NewClient(options...)
	if err != nil {
		return
	}
	bulkRequest := client.Bulk()
	indexName := "redis-slowlog-" + time.Now().Format("2006-01-02")
	for i := range logs {
		req := elastic.NewBulkIndexRequest().Index(indexName).Type("redis").Doc(logs[i])
		bulkRequest = bulkRequest.Add(req)
	}
	bulkResponse, err := bulkRequest.Do(context.Background())
	if err != nil {
		return
	}
	if bulkResponse.Errors {
		fmt.Println("写入elaticsearch失败", logs)
	}
	return
}
